package edu.unice.simul.controleur;

import java.util.ArrayList;
import java.util.List;

import edu.unice.simul.conf.GeneralSettings;
import edu.unice.simul.controleur.interfaces.PlayPauseControlerI;
import edu.unice.simul.controleur.interfaces.SpeedControlerI;
import edu.unice.simul.modele.Piece;
import edu.unice.simul.modele.Simulation;
import edu.unice.simul.modele.contenus.immobiles.Mur;
import edu.unice.simul.modele.contenus.immobiles.Sortie;
import edu.unice.simul.modele.contenus.mobiles.Humain;
import edu.unice.simul.util.Dim;
import edu.unice.simul.vue.SimulationFrame;

/**
 * Contrôleur principal de la simulation.
 * 
 * Ce contrôleur implémente les différentes interfaces correspondantes aux
 * principaux modules qui pourraient correspondrent à des sous contrôleurs.
 * 
 * @author Romain Ciaccafava
 * 
 */
public class SimulationControleur implements PlayPauseControlerI,
		SpeedControlerI {
	/**
	 * Fenêtre principale de l'application
	 */
	private SimulationFrame view;
	private Simulation model;
	private Piece piece;
	/**
	 * Enregistre la pièce afin de pouvoir relancer la simulation si nécéssaire
	 */
	private Piece sauvegardePiece;

	/**
	 * Ce constructeur est utilisé lors du lancement d'une simulation rapide.
	 */
	public SimulationControleur() {
		Dim dimmensionsDeLaPiece = new Dim(100, 100);
		piece = new Piece(dimmensionsDeLaPiece);

		initPiece(dimmensionsDeLaPiece);

		sauvegardePiece = new Piece(piece);

		this.model = new Simulation(piece, GeneralSettings.VITESSE_PAR_DEFAUT, this);
		this.view = new SimulationFrame(this, piece.getContenu(),
				piece.getDimmensions());

		connect();
		model.demarrer();
	}

	/**
	 * Ce constructeur est utilisé une fois l'initialisation effectuée.
	 * 
	 * @param piece
	 *          pièce initialisée
	 */
	public SimulationControleur(Piece piece) {
		sauvegardePiece = new Piece(piece);

		this.model = new Simulation(piece, GeneralSettings.VITESSE_PAR_DEFAUT, this);
		this.view = new SimulationFrame(this, piece.getContenu(),
				piece.getDimmensions());

		connect();
		model.demarrer();
	}

	/**
	 * Ajoute au modèle les écouteurs de la vue
	 */
	private void connect() {
		model.addMobilesChangedListener(view);
		model.addTimeListener(view.getTimeListener());
		model.addFinalStatsListener(view.getFinalStatsListener());
	}

	/**
	 * Initie la pièce pour la simulation rapide.
	 * 
	 * @param dimensionsDeLaPiece
	 *          Dimensions de la pièce
	 */
	@Deprecated
	private void initPiece(Dim dimensionsDeLaPiece) {
		Sortie s1 = new Sortie(new Dim(50, 79));
		piece.ajouterContenu(s1);
		Sortie s2 = new Sortie(new Dim(50, 78));
		piece.ajouterContenu(s2);
		Sortie s3 = new Sortie(new Dim(50, 77));
		piece.ajouterContenu(s3);
		Sortie s4 = new Sortie(new Dim(50, 76));
		//piece.ajouterContenu(s4);

		List<Dim> posSorties = new ArrayList<Dim>();
		posSorties.add(new Dim(s1.getPosition()));
		posSorties.add(new Dim(s2.getPosition()));
		posSorties.add(new Dim(s3.getPosition()));
		posSorties.add(new Dim(s4.getPosition()));

		List<Mur> murs = Mur.borduDeLaPiece(dimensionsDeLaPiece, posSorties);

		for (Mur m : murs)
			piece.ajouterContenu(m);

		for (int i = 20; i < 81; i++) {
			piece.ajouterContenu(new Mur(new Dim(i, 20)));
			piece.ajouterContenu(new Mur(new Dim(i, 80)));
			piece.ajouterContenu(new Mur(new Dim(20, i)));
			piece.ajouterContenu(new Mur(new Dim(80, i)));
		}
		for (int i = 25; i < 77; i++) {
			piece.ajouterContenu(new Mur(new Dim(76, i)));
			piece.ajouterContenu(new Mur(new Dim(25, i)));
			piece.ajouterContenu(new Mur(new Dim(i, 76)));
		}
		for (int i = 0; i <10 ; i++) {
			for(int j = 0; j<10; j++) {
		piece.ajouterContenu(new Humain(new Dim(40+2*i, 40+2*j), piece));
		}}
	}

	/**
	 * Met au pause la simulation
	 */
	@Override
	public void pause() {
		model.arreter();
	}

	/**
	 * Relance la simulation si elle était en pause
	 */
	@Override
	public void play() {
		model.demarrer();
	}

	/**
	 * Modifie la vitesse de la simulation
	 * 
	 * @param speed
	 *          correspond à une constante de la classe {@link GeneralSettings}
	 */
	@Override
	public void setSpeed(int speed) throws IllegalArgumentException {
		if (speed < 1 || speed > 5)
			throw new IllegalArgumentException(
					"La vitesse doit être comprise entre 1 et 5.");

		model.setSpeed(speed);
	}

	/**
	 * Lance l'évacuation de la pièce (irréversible)
	 */
	public void lancerEvacuation() {
		view.startLoading();
		model.lancerEvacuation();
	}

	/**
	 * Méthode utilisée lorsque le calcul du chemin le plus court est terminé
	 */
	public void stopLoading() {
		view.stopLoading();
	}

	/**
	 * Relance la simulation
	 */
	public void reset() {
		this.view.setVisible(false);
		this.piece = this.sauvegardePiece;
		this.sauvegardePiece = new Piece(this.piece);
		this.model = new Simulation(piece, GeneralSettings.VITESSE_PAR_DEFAUT, this);
		this.view = new SimulationFrame(this, piece.getContenu(),
				piece.getDimmensions());
		connect();
		this.model.demarrer();
	}
}
